﻿using System;
using System.Linq;
using System.Text;
using System.Threading;
using UtilTools.Enums;
using UtilTools.Structs;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace UtilTools
{
    /// <summary>
    /// 
    /// </summary>
    public class ECanPort : IDisposable
    {
        #region 引入DLL接口函数
        /// <summary>
        /// 打开设备
        /// </summary>
        /// <param name="DeviceType">设备类型</param>
        /// <param name="DeviceInd">设备id</param>
        /// <param name="Reserved">保留</param>
        [DllImport("ECANVCI.DLL", EntryPoint = "OpenDevice")]
        public static extern SEND_STATUS OpenDevice(UInt32 DeviceType, UInt32 DeviceInd, UInt32 Reserved);

        /// <summary>
        /// 关闭设备
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        [DllImport("ECANVCI.DLL", EntryPoint = "CloseDevice")]
        public static extern SEND_STATUS CloseDevice(UInt32 DeviceType, UInt32 DeviceInd);

        /// <summary>
        /// 初始化CAN通道
        /// </summary>
        /// <param name="DeviceType">设备类型</param>
        /// <param name="DeviceInd">设备id</param>
        /// <param name="CANInd">CAN通道id</param>
        /// <param name="InitConfig">配置参数</param>
        [DllImport("ECANVCI.DLL", EntryPoint = "InitCAN")]
        public static extern SEND_STATUS InitCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref INIT_CONFIG InitConfig);

        /// <summary>
        /// 读设备信息
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="Info"></param>
        [DllImport("ECANVCI.DLL", EntryPoint = "ReadBoardInfo")]
        public static extern SEND_STATUS ReadBoardInfo(UInt32 DeviceType, UInt32 DeviceInd, out BOARD_INFO Info);

        /// <summary>
        /// 读错误信息
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <param name="ErrInfo"></param>
        [DllImport("ECANVCI.DLL", EntryPoint = "ReadErrInfo")]
        public static extern SEND_STATUS ReadErrInfo(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, out ERR_INFO ErrInfo);

        /// <summary>
        /// 读取CAN状态
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <param name="CANStatus"></param>
        [DllImport("ECANVCI.DLL", EntryPoint = "ReadCanStatus")]
        public static extern SEND_STATUS ReadCanStatus(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, out CAN_STATUS CANStatus);

        /// <summary>
        /// 读取滤帧配置参数
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <param name="RefType"></param>
        /// <param name="Data"></param>
        [DllImport("ECANVCI.DLL", EntryPoint = "GetReference")]
        public static extern SEND_STATUS GetReference(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, UInt32 RefType, out FILTER_RECORD Data);

        /// <summary>
        /// 设置滤帧配置参数
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <param name="RefType">【0】标准帧【1】扩展帧</param>
        /// <param name="Data"></param>
        [DllImport("ECANVCI.DLL", EntryPoint = "SetReference")]
        public static extern SEND_STATUS SetReference(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, UInt32 RefType, ref FILTER_RECORD Data);

        /// <summary>
        /// 缓冲区帧数 10239 帧
        /// 缓冲区10240帧触发丢弃
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <returns></returns>
        [DllImport("ECANVCI.DLL", EntryPoint = "GetReceiveNum")]
        public static extern Int32 GetReceiveNum(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);

        /// <summary>
        /// 清空缓冲区
        /// </summary>
        /// <param name="DeviceType">设备类型</param>
        /// <param name="DeviceInd">设备id</param>
        /// <param name="CANInd">CAN通道id</param>
        [DllImport("ECANVCI.DLL", EntryPoint = "ClearBuffer")]
        public static extern SEND_STATUS ClearBuffer(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);

        /// <summary>
        /// 启动CAN通道
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        [DllImport("ECANVCI.DLL", EntryPoint = "StartCAN")]
        public static extern SEND_STATUS StartCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);

        /// <summary>
        /// 发送数据帧 7000帧
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <param name="Send"></param>
        /// <param name="length"></param>
        [DllImport("ECANVCI.DLL", EntryPoint = "Transmit")]
        public static extern SEND_STATUS Transmit(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, CAN_OBJ[] Send, UInt16 length);

        /// <summary>
        /// 接收数据帧,5帧，返回-1故障
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <param name="Receive"></param>
        /// <param name="length">单次读取帧数【封顶帧数】7为单次最多取7帧</param>
        /// <param name="WaitTimeMs">【0】不等待ms</param>
        [DllImport("ECANVCI.DLL", EntryPoint = "Receive")]
        public static extern SEND_STATUS Receive(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, out CAN_OBJ Receive, UInt32 length, UInt32 WaitTimeMs);

        /// <summary>
        /// 复位CAN通道
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <returns></returns>
        [DllImport("ECANVCI.DLL", EntryPoint = "ResetCAN")]
        public static extern SEND_STATUS ResetCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);

        #endregion

        /// <summary>
        /// 
        /// </summary>
        public Boolean EnableProc;
        /// <summary>
        /// 
        /// </summary>
        public const Int32 REC_MSG_BUF_MAX = 0x2710;
        /// <summary>
        /// 
        /// </summary>
        public const Int32 SEND_MSG_BUF_MAX = 0x2710;

        /// <summary>
        /// 
        /// </summary>
        public CAN_OBJ[] RecBuffer;
        /// <summary>
        /// 
        /// </summary>
        public UInt32 RecBufferHead;
        /// <summary>
        /// 
        /// </summary>
        public UInt32 RecBufferTail;

        /// <summary>
        /// 
        /// </summary>
        public CAN_OBJ[] SendBuffer;
        /// <summary>
        /// 
        /// </summary>
        public UInt32 SendBufferHead;
        /// <summary>
        /// 
        /// </summary>
        public UInt32 SendBufferTail;

        readonly Timer RecTimer;
        readonly Timer SendTimer;
        readonly AutoResetEvent RecEvent;
        readonly TimerCallback RecTimerDelegate;
        readonly AutoResetEvent SendEvent;
        readonly TimerCallback SendTimerDelegate;

        /// <summary>
        /// 
        /// </summary>
        public ECanPort()
        {
            this.RecBuffer = new CAN_OBJ[REC_MSG_BUF_MAX];
            this.SendBuffer = new CAN_OBJ[SEND_MSG_BUF_MAX];

            this.SendBufferHead = 0;
            this.SendBufferTail = 0;

            this.RecBufferHead = 0;
            this.RecBufferTail = 0;

            this.EnableProc = false;
            this.RecEvent = new AutoResetEvent(false);
            this.RecTimerDelegate = new TimerCallback(this.RecTimer_Tick);
            this.RecTimer = new Timer(this.RecTimerDelegate, this.RecEvent, 0, 20);

            this.SendEvent = new AutoResetEvent(false);
            this.SendTimerDelegate = new TimerCallback(this.SendTimer_Tick);
            this.SendTimer = new Timer(this.SendTimerDelegate, this.SendEvent, 0, 20);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="state"></param>
        private void SendTimer_Tick(object state)
        {
            if (this.EnableProc) { this.SendMsg(); }
        }

        /// <summary>
        /// 
        /// </summary>
        private void SendMsg()
        {
            Int32 SendCunt = 0;
            CAN_OBJ[] MsgObj = new CAN_OBJ[2];
            do
            {
                if (this.SendBufferHead == this.SendBufferTail) { break; }
                MsgObj[0] = this.SendBuffer[this.SendBufferTail];
                MsgObj[1] = this.SendBuffer[this.SendBufferTail];
                this.SendBufferTail += 1; UInt16 len = 1;
                if (this.SendBufferTail >= SEND_MSG_BUF_MAX) { this.SendBufferTail = 0; }
                if (Transmit(1, 0, 0, MsgObj, len) != SEND_STATUS.OK)
                {
                    SysLog.Error("USBCAN SEND ERROR");
                }
                SendCunt++;
            }
            while (SendCunt < 200);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        private void RecTimer_Tick(Object obj)
        {
            if (this.EnableProc) { this.ReadMsg(); }
        }

        /// <summary>
        /// 
        /// </summary>
        private void ReadMsg()
        {
            Int32 ReadCunt = 0;
            do
            {
                UInt32 RecLen = 1;
                if (!(Receive(1, 0, 0, out CAN_OBJ MsgObj, RecLen, 1) == SEND_STATUS.OK)) { break; }
                if (RecLen == 0) break;
                this.RecBuffer[this.RecBufferHead].ID = MsgObj.ID;
                this.RecBuffer[this.RecBufferHead].Data = MsgObj.Data;
                this.RecBuffer[this.RecBufferHead].DataLen = MsgObj.DataLen;
                this.RecBuffer[this.RecBufferHead].ExternFlag = MsgObj.ExternFlag;
                this.RecBuffer[this.RecBufferHead].RemoteFlag = MsgObj.RemoteFlag;
                this.RecBuffer[this.RecBufferHead].TimeStamp = MsgObj.TimeStamp;
                this.RecBuffer[this.RecBufferHead].Reserved = MsgObj.Reserved;
                this.RecBuffer[this.RecBufferHead].TimeFlag = MsgObj.TimeFlag;
                this.RecBufferHead += 1;
                if (this.RecBufferHead >= REC_MSG_BUF_MAX) { this.RecBufferHead = 0; }
                ReadCunt++;
            }
            while (ReadCunt < 500);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {

            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}
